using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
using Microsoft.SharedSource.SqlServer.MDDE.Middleware;

namespace Microsoft.SharedSource.SqlServer.MDDE.ManagementStudio
{
    public partial class ImportTemplate : Form
    {
        #region Fields and enumerations

        enum TemplateLocation
        {
            FileSystem,
            SQLServer
        }

        string templatePath;
        TemplateLocation location;
        string templateBody;
        XmlDocument templateConfigurations;
        XmlElement rootElement;
        string repositoryConnectionString;

        #endregion

        #region Event handlers

        private void btnBrowse_Click(object sender, EventArgs e)
        {
            OpenFileDialog fd = new OpenFileDialog();
            fd.Title = "Import Template";
            fd.Filter = "SSIS Packages|*.dtsx";
            fd.Multiselect = false;
            fd.ShowDialog();

            Cursor.Current = Cursors.WaitCursor;

            // TODO: Gray out all the controls here.

            this.Refresh();

            if (!string.IsNullOrEmpty(fd.FileName))
            {
                templatePath = fd.FileName;
                txtPackagePath.Text = templatePath;
                CheckForValidInputsAndLoadTemplate();
            }

            // TODO: Reenable controls here.

            Cursor.Current = Cursors.Default;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void trvTemplateComponents_AfterSelect(
            object sender, TreeViewEventArgs e)
        {
            // Change the label texts.
            lblComponentName.Text = e.Node.Name;
            lblURI.Text = e.Node.FullPath;
            dgProperties.Rows.Clear();

            // Disable the mappings checkbox if the selected 
            // node is not of the type "Component".
            if (((XmlNode)e.Node.Tag).Attributes["Type"].Value.Equals(
                "Component", StringComparison.InvariantCultureIgnoreCase))
            {
                chkConfigurableMappings.Enabled = true;
                chkCollections.Enabled = true;
            }
            else
            {
                chkConfigurableMappings.Enabled = false;
                chkCollections.Enabled = false;
            }

            // Populate the grid and checkbox.
            XmlNodeList propertyNodes =
                ((XmlNode)(e.Node.Tag)).SelectNodes("./Property");

            // See if we have entry for this node in templateConfiguration.
            XmlNode configurationNode = templateConfigurations.
                SelectSingleNode("//ConfigurableComponent[@ComponentUri=\"" +
                ((XmlNode)e.Node.Tag).Attributes["Uri"].Value + "\"]");

            // Entry not found.
            if (configurationNode == null)
            {
                // Populate the grid with unchecked first column.
                foreach (XmlNode childNode in propertyNodes)
                    dgProperties.Rows.Add(null,
                        childNode.Attributes["Name"].Value);

                // Clear off the mappings checkbox. This might 
                // invoke the checkbox event handler.
                chkConfigurableMappings.Checked = false;

                // Clear off the collections checkbox. This might 
                // invoke the checkbox event handler.
                chkCollections.Checked = false;
            }
            // Entry found!
            else
            {
                XmlNode configurableProperty;
                // Populate the grid taking into account 
                // the earlier property values.
                foreach (XmlNode childNode in propertyNodes)
                {
                    // Check if we have this configurable 
                    // property already marked.
                    configurableProperty =
                        configurationNode.SelectSingleNode(
                        "./ConfigurableProperty[@Name=\"" +
                        childNode.Attributes["Name"].Value + "\"]");

                    if (configurableProperty == null)
                        dgProperties.Rows.Add(null,
                            childNode.Attributes["Name"].Value);
                    else
                        dgProperties.Rows.Add(true,
                            childNode.Attributes["Name"].Value);
                }

                // Populate the mappings checkbox with earlier 
                // mappings information.
                // CAUTION: The statements below may cause the 
                // checkbox event handler to execute again.
                if (configurationNode.Attributes["ContainsMappings"].
                    Value == "True")
                    chkConfigurableMappings.Checked = true;
                else
                    chkConfigurableMappings.Checked = false;

                // Populate the collections checkbox with earlier 
                // collections information.
                // CAUTION: The statements below may cause the 
                // checkbox event handler to execute again.
                if (configurationNode.Attributes["ContainsCollections"].
                    Value == "True")
                    chkCollections.Checked = true;
                else
                    chkCollections.Checked = false;
            }
        }

        private void chkConfigurableMappings_CheckedChanged(
            object sender, EventArgs e)
        {
            // Save off the change to template configurations.
            ApplyChangeToTemplateConfiguration(
                (XmlNode)trvTemplateComponents.SelectedNode.Tag,
                null,
                chkConfigurableMappings.Checked ? "True" : "False",
                null,
                chkConfigurableMappings.Checked);
        }

        private void chkCollections_CheckedChanged(object sender, EventArgs e)
        {
            // Save off the change to template configurations.
            ApplyChangeToTemplateConfiguration(
                (XmlNode)trvTemplateComponents.SelectedNode.Tag,
                null,
                null,
                chkCollections.Checked ? "True" : "False",
                chkCollections.Checked);
        }

        private void dgProperties_CellValueChanged(
            object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex < 0 || e.RowIndex > dgProperties.Rows.Count - 1)
                return;

            // Save off the change to template configurations.
            ApplyChangeToTemplateConfiguration(
                (XmlNode)trvTemplateComponents.SelectedNode.Tag,
                dgProperties.Rows[e.RowIndex].
                Cells["propertyName"].Value.ToString(),
                null, null,
                (Boolean)dgProperties.Rows[e.RowIndex].
                Cells["isConfigurable"].Value);
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            // Verify that we have all the inputs.
            if (txtTemplateName.Text == string.Empty)
            {
                MessageBox.Show("Please specify a template name.",
                    "Insufficient Information", MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);

                return;
            }

            if (trvTemplateComponents.Nodes.Count <= 0)
            {
                MessageBox.Show("Please select a template body.",
                    "Insufficient Information", MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation);

                return;
            }

            // Add the template name to template configurations.
            XmlAttribute templateName =
                templateConfigurations.CreateAttribute("ComponentName");
            templateName.Value = txtTemplateName.Text;
            rootElement.Attributes.Append(templateName);

            // Pass on the template configurations to middleware
            // so that it can import the template into the database.
            OperationResult result = MDDEManager.AddNewTemplate(
                repositoryConnectionString, txtTemplateName.Text,
                templateBody, templateConfigurations);

            if (result.SuccessStatus == false)
            {
                StringBuilder errorLog = new StringBuilder();
                foreach (string logEntry in result.OperationLog)
                    errorLog.Append(logEntry);

                MessageBox.Show("Template could not be imported." +
                    errorLog.ToString(), "Error", MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

                return;
            }
            else
            {
                MessageBox.Show("Template successfully imported",
                    "Success", MessageBoxButtons.OK,
                    MessageBoxIcon.Information);

                this.Close();
            }
        }

        #endregion

        #region Helper methods

        public ImportTemplate(string repositoryConnectionString)
        {
            InitializeComponent();

            this.repositoryConnectionString = repositoryConnectionString;

            //For now, the location can only be File sytem.
            location = TemplateLocation.FileSystem;

            // Initialize templateconfigurations.
            templateConfigurations = new XmlDocument();
            rootElement = templateConfigurations.CreateElement("Template");
            templateConfigurations.AppendChild(rootElement);
        }

        private void CheckForValidInputsAndLoadTemplate()
        {
            // If template location is 'file system'
            if (location == TemplateLocation.FileSystem)
            {
                // Verify that file exists.
                if (!string.IsNullOrEmpty(templatePath) &&
                    File.Exists(templatePath))
                {
                    StreamReader rdr = new StreamReader(templatePath);
                    templateBody = rdr.ReadToEnd();

                    // TODO: Validate the input body against SSIS package xsd.

                    LoadTemplate();
                }
            }
        }

        private void LoadTemplate()
        {
            if (string.IsNullOrEmpty(templateBody))
                return;

            XmlDocument templateTree =
                MDDEManager.GetPackageTree(templateBody);

            // Populate the template information.
            trvTemplateComponents.Nodes.Clear();
            rootElement.RemoveAll();
            lblComponentName.Text = string.Empty;
            lblURI.Text = string.Empty;
            dgProperties.Rows.Clear();

            TemplateComponentNode tn =
                new TemplateComponentNode(templateTree.DocumentElement);

            tn.PopulateSubtree();

            trvTemplateComponents.Nodes.Add(tn);
        }

        private void ApplyChangeToTemplateConfiguration(
            XmlNode componentNode, string propertyName,
            string containsMappings, string containsCollections,
            bool includeChangeInTemplateConfigurations)
        {
            // Pick out the component Uri.
            string componentUri = componentNode.Attributes["Uri"].Value;

            // See if we already have this component enlisted.
            XmlNode configurableComponent = rootElement.
                SelectSingleNode("//ConfigurableComponent[@ComponentUri=\"" +
                componentUri + "\"]");

            // Create a new node for this component 
            // if it does not exist already.
            if (configurableComponent == null)
            {
                configurableComponent =
                    UIHelper.AppendChildElement(rootElement, "ConfigurableComponent",
                    new string[] { 
                        "ComponentName",componentNode.Attributes["Name"].Value,
                        "ComponentClassId",componentNode.Attributes["ClassId"].Value,
                        "ComponentUri",componentNode.Attributes["Uri"].Value,
                        "ComponentType",componentNode.Attributes["Type"].Value,
                        "RowsetType",componentNode.Attributes["RowsetDescriptorType"].Value,
                        "ContainsMappings",componentNode.Attributes["ContainsMappings"].Value,
                        "ContainsCollections",componentNode.Attributes["ContainsCollections"].Value});

                //configurableComponent = templateConfigurations.
                //    CreateElement("ConfigurableComponent");

                // Copy over all the attributes.
                //foreach (XmlAttribute attComponentNode in componentNode.Attributes)
                //{
                //    XmlAttribute attConfigurableComponent =
                //        templateConfigurations.CreateAttribute(
                //        attComponentNode.Name);

                //    attConfigurableComponent.Value =
                //        attComponentNode.Value;

                //    configurableComponent.Attributes.
                //        Append(attConfigurableComponent);
                //}

                //rootElement.AppendChild(configurableComponent);
            }

            // Add/Remove configurable property.
            XmlNode configurableProperty;
            if (propertyName != null)
            {
                // Get the configurable property node.
                configurableProperty =
                    configurableComponent.SelectSingleNode(
                    "./ConfigurableProperty[@Name=\"" +
                    propertyName + "\"]");

                // Add the configuration if the change 
                // is for inclusion of the property.
                if ((configurableProperty == null) &&
                    includeChangeInTemplateConfigurations)
                {
                    configurableProperty = templateConfigurations.
                        CreateElement("ConfigurableProperty");

                    XmlAttribute attrib = templateConfigurations.
                        CreateAttribute("Name");

                    attrib.Value = propertyName;

                    configurableProperty.Attributes.Append(attrib);

                    configurableComponent.AppendChild(configurableProperty);
                }
                // Remove the configuration if the change 
                // is for exclusion of the property.
                if ((configurableProperty != null) &&
                    !includeChangeInTemplateConfigurations)
                    configurableProperty.ParentNode.RemoveChild(
                        configurableProperty);
            }

            // Update the mappings information.
            if (containsMappings != null)
                configurableComponent.Attributes["ContainsMappings"].
                    Value = containsMappings;

            // Update the collections information.
            if (containsCollections != null)
                configurableComponent.Attributes["ContainsCollections"].
                    Value = containsCollections;

            // If there are no configurable properties for this 
            // component, neither does it contain mappings,
            // nor it contains configurable column collections, remove it.
            if (!configurableComponent.HasChildNodes &&
                configurableComponent.Attributes["ContainsMappings"].
                Value == "False" &&
                configurableComponent.Attributes["ContainsCollections"].
                Value == "False")
                configurableComponent.ParentNode.RemoveChild(
                    configurableComponent);
        }

        #endregion
    }

    internal sealed class TemplateComponentNode : TreeNode
    {
        internal TemplateComponentNode(XmlNode treeNode)
        {
            if (treeNode.Attributes["Type"].Value == "Collection" ||
                treeNode.Attributes["Type"].Value == "Package")
            {
                this.ImageIndex = 2;
                this.SelectedImageIndex = 2;
            }
            else
            {
                this.ImageIndex = 4;
                this.SelectedImageIndex = 4;
            }

            this.Name = this.Text = treeNode.Attributes["Name"].Value;

            this.Tag = treeNode;
        }

        internal void PopulateSubtree()
        {
            if (((XmlNode)this.Tag).Attributes["Type"].Value == "Component")
                return;

            this.Nodes.Clear();

            XmlNodeList childNodes =
                ((XmlNode)(this.Tag)).SelectNodes("./Node");

            foreach (XmlNode childNode in childNodes)
            {
                TemplateComponentNode childComponentNode =
                    new TemplateComponentNode(childNode);

                childComponentNode.PopulateSubtree();

                this.Nodes.Add(childComponentNode);
            }
        }
    }
}